package br.com.vpsa.previdordevendas.model.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import br.com.vpsa.previdordevendas.model.entity.Declinio;
import br.com.vpsa.previdordevendas.model.entity.FaturamentoMensal;
import br.com.vpsa.previdordevendas.model.entity.Inflacao;
import br.com.vpsa.previdordevendas.model.entity.Investimento;
import br.com.vpsa.previdordevendas.model.pojo.Tendencia;
import br.com.vpsa.previdordevendas.model.service.DeclinioService;
import br.com.vpsa.previdordevendas.model.service.FaturamentoMensalService;
import br.com.vpsa.previdordevendas.model.service.InflacaoService;
import br.com.vpsa.previdordevendas.model.service.InvestimetoService;
import br.com.vpsa.previdordevendas.model.service.TendenciaService;
import br.com.vpsa.previdordevendas.model.util.CalendarUtils;

@Service
public class TendenciaServiceImpl implements TendenciaService
{
	@Autowired
	private FaturamentoMensalService faturamentoMensalService;
	
	@Autowired
	private InflacaoService inflacaoService;
	
	@Autowired
	private InvestimetoService investimetoService;

	@Autowired
	private DeclinioService declinioService;
	
	public Tendencia gerarTendencia( String base, Long idEntidade, int ano )
	{
		Tendencia tendencia = new Tendencia();
		
		List<Investimento> investimentos =  investimetoService.listar( base, idEntidade );
		
		List<Declinio> declinios = declinioService.listar( base, idEntidade );
		
		for( Integer mes : CalendarUtils.meses )
		{
			Calendar dataGrafico = getDataGrafico(mes,ano);
			
			List<FaturamentoMensal> faturamentosMes = faturamentoMensalService.listarFaturamentoDeAnosAnterioresParaUmMes(base, idEntidade, mes, ano);
		
			if( ! faturamentosMes.isEmpty() )
			{
				aplicarTendenciasParaMesesSemFaturamento(faturamentosMes, base, dataGrafico);
				
				BigDecimal tendenciaFaturamento = gerarTendenciaMensal(base, ano, faturamentosMes);
				tendencia.getFaturamento().add(tendenciaFaturamento);
				
				BigDecimal ameaca = aplicarAmeaca(tendenciaFaturamento , dataGrafico, declinios );
				if( tendenciaFaturamento.compareTo(ameaca) > 0 ){
					tendencia.getAmeacas().add(ameaca);
				}else{
					tendencia.getAmeacas().add(null);
				}
				
				BigDecimal investimento = aplicarInvestimentos(ameaca, dataGrafico, investimentos);
				if( investimento.compareTo(ameaca) > 0 ){
					tendencia.getInvestimentos().add(investimento);
				}else{
					tendencia.getInvestimentos().add(null);
				}
			}
			else {
				tendencia.getFaturamento().add(null);
				tendencia.getInvestimentos().add(null);
				tendencia.getAmeacas().add(null);
			}
		}
		
		return tendencia;
	}

	private BigDecimal aplicarAmeaca(BigDecimal tendenciaFaturamento,	Calendar dataGrafico, List<Declinio> declinios ) 
	{
		BigDecimal totalDeclinio = BigDecimal.ZERO;
		
		for( Declinio declinio : declinios )
		{
			Calendar dataInicial =  CalendarUtils.getCalendar(declinio.getDataInicial());
			Calendar dataFinal =  declinio.getCalendarDataFinal();
			
			if( dataGrafico.after(dataInicial) && ( dataFinal == null || dataGrafico.before(dataFinal))  ){
				
				BigDecimal valorDeclinio = tendenciaFaturamento.multiply( declinio.getPorcentagemReal() );
				
				totalDeclinio = totalDeclinio.add(valorDeclinio);
			}
		}
		
		return tendenciaFaturamento.subtract( totalDeclinio ) ;
	}

	private BigDecimal gerarTendenciaMensal(String base, int ano, List<FaturamentoMensal> faturamentosMes) 
	{
		List<BigDecimal> variacoesMensais = calculaVariacaoMensal( faturamentosMes );
		
		BigDecimal mediaPonderadaVariacao = calulaMediaPonderada(variacoesMensais);
		
		BigDecimal faturamentoMensalAnoAnterior = faturamentosMes.get(faturamentosMes.size()-1).getValorTotal();
		
		BigDecimal tendenciaFaturamento = faturamentoMensalAnoAnterior.add( mediaPonderadaVariacao );
		
		tendenciaFaturamento = aplicarInflacao(tendenciaFaturamento, base, ano);
		
		return tendenciaFaturamento;
	}

	private Calendar getDataGrafico(int mes, int ano){
		Calendar dataGrafico = Calendar.getInstance();
		dataGrafico.set(Calendar.MONTH, mes);
		dataGrafico.set(Calendar.YEAR, ano);
		dataGrafico.set(Calendar.DAY_OF_MONTH, 15);
		return dataGrafico;
	}
	
	private void aplicarTendenciasParaMesesSemFaturamento( List<FaturamentoMensal> faturamentosMes, String base , Calendar dataGrafico ) 
	{
		FaturamentoMensal ultimoFaturamentoMensal = faturamentosMes.get( faturamentosMes.size()-1 );
		
		for( int ano = ultimoFaturamentoMensal.getAno()+1 ; ano < dataGrafico.get(Calendar.YEAR) ; ano++  )
		{
			BigDecimal tendencia = gerarTendenciaMensal(base, ano, faturamentosMes);
			
			faturamentosMes.add( new FaturamentoMensal(ano, dataGrafico.get(Calendar.MONTH), tendencia)  );
		}
	}

	private BigDecimal aplicarInvestimentos(BigDecimal tendenciaFaturamento, Calendar dataGrafico , List<Investimento> investimentos) 
	{
		for( Investimento investimento : investimentos )
		{
			Calendar dataInicialInvestimento =  CalendarUtils.getCalendar(investimento.getDataInvestimento());
			
			if( dataGrafico.after(dataInicialInvestimento) && dataGrafico.before(investimento.getDataFimInvestimento())  )
			{
				BigDecimal valorInvestimentoMes = investimento.getValorMes( CalendarUtils.mesesEntre(dataInicialInvestimento, dataGrafico) );
				
				tendenciaFaturamento = tendenciaFaturamento.add(valorInvestimentoMes);
			}
		}
		
		return tendenciaFaturamento;
	}

	private BigDecimal aplicarInflacao(BigDecimal tendenciaFaturamento, String base, int ano) 
	{
		BigDecimal valorInflacao = Inflacao.INFLACAO_PADRAO;
		
		Inflacao inflacao = inflacaoService.getInflacao(base, ano);
		
		if( inflacao != null ){
			valorInflacao = inflacao.getValor();
		}
		
		return tendenciaFaturamento.multiply(BigDecimal.ONE.add(valorInflacao.divide(new BigDecimal(100))));
	}

	private List<BigDecimal> calculaVariacaoMensal( List<FaturamentoMensal> faturamentosMes )
	{
		List<BigDecimal> variacoesMensais = new ArrayList<BigDecimal>();
		
		if( faturamentosMes.size() == 1 ){
			return Collections.singletonList(BigDecimal.ZERO);
		}
		
		for( int i=1 ; i < faturamentosMes.size() ; i++ )
		{
			FaturamentoMensal faturamentoMensal = faturamentosMes.get(i);
			FaturamentoMensal faturamentoMensalAnterior = faturamentosMes.get(i-1);
			
			BigDecimal variacao = faturamentoMensal.getValorTotal().subtract( faturamentoMensalAnterior.getValorTotal() );
			
			variacoesMensais.add(variacao);
		}
		
		return variacoesMensais;
	}
	
	private BigDecimal calulaMediaPonderada( List<BigDecimal> valores )
	{
		BigDecimal peso = BigDecimal.ONE;
		BigDecimal somatorioVariacaoPeso = BigDecimal.ZERO;
		BigDecimal somatorioPesos = BigDecimal.ZERO;
		
		for( BigDecimal valor : valores )
		{
			somatorioVariacaoPeso = somatorioVariacaoPeso.add( valor.multiply(peso) );
			somatorioPesos = somatorioPesos.add(peso);
			peso = peso.add(BigDecimal.ONE);
		}
		
		return somatorioVariacaoPeso.divide( somatorioPesos, 2, RoundingMode.HALF_EVEN );	
	}
	
}
